﻿#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
// 
// Jazz is open software: you can redistribute it and/or modify it 
// under the terms of the GNU Affero General Public License (AGPL) as published by 
// the Free Software Foundation, version 3 of the License.
// 
// Jazz is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty 
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
//  See the GNU Affero General Public License (AGPL) for more details.
// 
// You should have received a copy of the GNU General Public 
// License along with Jazz.  If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT. 
//</copyright>
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Aspects.Configuration;
using PostSharp.Aspects.Internals;
using PostSharp.Aspects.Dependencies;
using PostSharp.Extensibility;
using A = TrackerRealm.Jazz.Client.FormatHelper;

//[assembly: TrackerRealm.Jazz.Client.AclExecuteAttribute(AttributeExclude = true, AttributePriority = 0,
//            AttributeTargetMemberAttributes = MulticastAttributes.CompilerGenerated)]
//[assembly: TrackerRealm.Jazz.Client.AclExecuteAttribute(AspectPriority = 0,
//    AttributeExclude = true, AttributeTargetMembers = "regex:get_.*|set_*")]


namespace TrackerRealm.Jazz.Client
{
    #region FormatHelper
    /// <summary>
    /// 
    /// </summary>
    public static class FormatHelper
    {
        /// <summary>
        /// Generates a string describing the Jazz object and the action string.
        /// </summary>
        /// <param name="jObj"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static string Prefix(jObject jObj, string action)
        {
            return jObj.ToString() + "." + action + ":\t\t";
        }
    }
    #endregion
    #region Jazz
    /// <summary>
    /// This attribute marks classes as a Jazz object.  The class must inherit from '<see cref="jObject"/>'.
    /// <para>All class members in a '<see cref="jObject"/>' inheritance chain must be marked with this attribute.</para>
    /// <para>This attribute supports persistence features of Jazz.</para>
    /// </summary>
    [Serializable]
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Class|AttributeTargets.Property|AttributeTargets.Method)]
#if DEBUG
    // generates better cross checks in 'jClass'
    [MulticastAttributeUsage(MulticastTargets.Method|MulticastTargets.InstanceConstructor|MulticastTargets.StaticConstructor, PersistMetaData = true)]
#else
    [MulticastAttributeUsage(MulticastTargets.Method|MulticastTargets.InstanceConstructor|MulticastTargets.StaticConstructor)]
#endif
    public class JazzAttribute : OnMethodBoundaryAspect
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jazzObject"></param>
        /// <param name="states"></param>
        /// <returns></returns>
        public static bool IsAccessible_States(jObject jazzObject, IEnumerable<string> states)
        {
            return states.Contains(jazzObject.CurrentState.Name);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jazzObject"></param>
        /// <param name="roles"></param>
        /// <returns></returns>
        public static bool IsAccessible_Roles(jObject jazzObject, IEnumerable<string> roles)
        {
            if (jazzObject.Class == null)
            {
                string msg = string.Format("Expecting a non-null 'ClientNexus' property.  Has the Jazz Object been bound? - {0}",
                    jazzObject.ID);
                if (jazzObject.IsAccessible || jazzObject.IsReadOnly)
                    msg = msg + ",  " + jazzObject.FullName;
                throw new InvalidOperationException(msg);
            }
            return (roles.Intersect(jazzObject.ClientNexus._LoginRoles.Select(r => r.Name)).Count() > 0);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        public override void OnEntry(MethodExecutionArgs args)
        {
            // performance can be improved.
            jObject wf = (jObject)args.Instance;
            if (wf != null &&  wf.Flags.HasFlag(JazzFlagsType.Bound ))//wf.ClientNexus != null)
            {

                #region Accessible Check
                if (!wf.IsAccessible)
                {
                    // can't display name, because not accessible
                    throw new AccessViolationException(string.Format("The Jazz object, '{0}', is not accessible.", wf.ID));
                }
                #endregion
                #region Readonly Check
                if (wf.IsReadOnly && !this.isGetMethod && !this.isReadOnlyValid)
                {
                    if(args.Method.Name != "ToString")
                        throw new AccessViolationException(string.Format("Methods cannot be called on a readonly object: {0}", wf.FullName));
                }
                #endregion
                else
                {
                    if (this.roles != null)
                    {
                        #region Roles Check
#if DEBUG
                        if ((wf.ClientNexus.Verbose & VerboseType.Roles) == VerboseType.Roles)
                        {
                            Console.WriteLine(string.Format(
                                "{0}Execute Method '{1}' - Roles checked ",
                                A.Prefix(wf, "GrantRole"),
                                args.Method.Name));
                        }
#endif
                        if (!IsAccessible_Roles(wf, this.roles))
                        {
                            string exMsg = string.Format("{0}Access is not allowed to '{1}'.  The allowed roles are '{2}'.  The login roles are: '{3}'.",
                                                            A.Prefix(wf, "AccessViolation"),
                                                            args.Method.Name,
                                                            string.Join("', '", this.roles),
                                                            string.Join("', '", wf.ClientNexus._LoginRoles.Select<jRoleBase, string>(r => r.Name)));
#if DEBUG
                            if (((wf.ClientNexus.Verbose & VerboseType.Roles) == VerboseType.Roles))
                            {
                                Console.WriteLine(exMsg);
                            }
#endif
                            throw new AccessViolationException(exMsg);
                        }
                        #endregion
                    }
                    if (this.states != null)
                    {
                        #region States Check
#if DEBUG
                        if (((wf.ClientNexus.Verbose & VerboseType.States) == VerboseType.States))
                        {
                            Console.WriteLine(string.Format(
                           "{0}Execute Method '{1}' - State checked - CurrentState = {2}",
                               A.Prefix(wf, "GrantState"),
                               args.Method.Name, ((jObject)args.Instance).CurrentState.Name));
                        }
#endif
                        if (!IsAccessible_States(wf, this.states))
                        {
                            string exMsg = string.Format("{0}Access is not allowed to '{1}'.  The current state is {2}.  Valid states are: '{3}'.",
                                                            A.Prefix(wf, "AccesViolation"),
                                                            args.Method.Name,
                                                            ((jObject)args.Instance).CurrentState.Name,
                                                            string.Join("', '", this.states));
#if DEBUG
                            if (((jObject)args.Instance).ClientNexus != null && ((((jObject)args.Instance).ClientNexus.Verbose & VerboseType.Properties) == VerboseType.Properties))
                            {
                                Console.WriteLine(exMsg);
                            }
#endif
                            throw new AccessViolationException(exMsg);
                        }
                        #endregion
                    }
                    if (this.propertyGrant != null)
                    {
                        #region Property Check
                        // needs to be set up first time through, not every time
                        MethodInfo m = wf.Class.GetMethodInfo("get_" + this.propertyGrant);
                        object prop = m.Invoke(wf, new object[0]);
                        if (prop is jProfileBase)
                        {
                            if ((jProfileBase)prop != wf.ClientNexus._LoginProfile)
                                throw new AccessViolationException(string.Format("Access is not granted. The logged in user must be '{0}'.", ((jObject)prop).FullName));
                        }
                        else if(prop is IEnumerable<jProfileBase>)
                        {
                            bool isValidUser = false;
                            foreach (jProfileBase user in (IEnumerable<jProfileBase>)prop)
                            {
                                if (user == wf.ClientNexus._LoginProfile)
                                {
                                    isValidUser = true;
                                    break;
                                }
                            }
                            if (!isValidUser)
                            {
                                string[] users = ((IEnumerable<jProfileBase>)prop).Select<jProfileBase, String>(u => u._User.FullName).ToArray();
                                throw new AccessViolationException(string.Format("Access is not granted. The logged in user must one of the following '{0}'.", 
                                    string.Join("', '",users)));
                            }
                        }
                        else if (prop is bool)
                        {
                            if (!(bool)prop)
                                throw new AccessViolationException(string.Format("Access is not granted. The property '{0}' is false.", this.propertyGrant));
                        }
                        else if (prop is IEnumerable<jRoleBase>)
                        {
                            if (!IsAccessible_Roles(wf, ((IEnumerable<jRoleBase>)prop).Select<jRoleBase, string>(r => r.Name)))
                            {
                                string[] roles = ((IEnumerable<jRoleBase>)prop).Select<jRoleBase, string>(r => r.Name).ToArray();

                                throw new AccessViolationException(string.Format("Access is not granted. The property '{0}' returned roles '{1}' that did not match the logged in user's roles.",
                                    this.propertyGrant,
                                    String.Join("' ,'", roles)));
                            }
                        }
                        else if (prop is IEnumerable<jState>)
                        {
                            if (!IsAccessible_States(wf, ((IEnumerable<jState>)prop).Select<jState, string>(r => r.Name)))
                            {
                                string[] roles = ((IEnumerable<jRoleBase>)prop).Select<jRoleBase, string>(r => r.Name).ToArray();

                                throw new AccessViolationException(string.Format("Access is not granted. The property '{0}' returned roles '{1}' that did not match the logged in user's roles.",
                                    this.propertyGrant,
                                    String.Join("' ,'", roles)));
                            }
                        }
                        else throw new InvalidOperationException(string.Format("The property '{0}' returned an unexpected type, '{1}'.", this.propertyGrant, prop.GetType().FullName));
                        #endregion
                    }
                }
#if DEBUG
               
                    if (wf != null && wf.Flags.HasFlag(JazzFlagsType.Bound) && wf.ClientNexus != null &&(( wf.ClientNexus.Verbose & VerboseType.Properties) == VerboseType.Properties))
                    {
                        if (args.Method.Name.StartsWith("set_"))
                        {
                            Console.WriteLine(string.Format("{0}Set Property - {1} = {2}", A.Prefix(wf, "Change"), args.Method.Name, args.Arguments[0]));
                        }
                    }
               
#endif
            }
            base.OnEntry(args);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        public override void OnExit(MethodExecutionArgs args)
        {
            if (args.Method.Name.StartsWith("set_"))
            {
                jObject wf = args.Instance as jObject;
                if(wf!= null)
                    wf.OnPropertyChange(new PropertyChangedEventArgs(args.Method.Name.Remove(0, 4)));
                if (wf.Class != null && wf.Class.Rules.Count > 1)
                    wf.Class.Rules.ExecuteRules(wf);
            }
            base.OnExit(args);
        }
        private string[] roles;
        private string[] states;
        private string propertyGrant;
        private bool isGetMethod = false;
        private bool isReadOnlyValid = false;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="method"></param>
        public override void RuntimeInitialize(System.Reflection.MethodBase method)
        {
            object[] aArray = method.GetCustomAttributes(typeof(GrantRolesAttribute), true);
            if (aArray.Length > 0)
            {
                roles = ((GrantRolesAttribute)aArray[0]).Roles;
            }
            object[] sArray = method.GetCustomAttributes(typeof(GrantStatesAttribute), true);
            if (sArray.Length > 0)
            {
                states = ((GrantStatesAttribute)sArray[0]).States;
            }
            object[] sProperty = method.GetCustomAttributes(typeof(GrantPropertyAttribute), true);
            if (sProperty.Length > 0)
            {
                propertyGrant = ((GrantPropertyAttribute)sProperty[0]).Property;
            }
            if(method.IsAccessorMethodAttribute())
            {
                this.isReadOnlyValid = true;
            }
            if (roles == null && states == null && propertyGrant == null && (method.Name.StartsWith("set_") || method.Name.StartsWith("get_")))
            {
                PropertyInfo prop = ((MethodInfo)method).GetAccessorProperty();
                object[] pArray = prop.GetCustomAttributes(typeof(GrantRolesAttribute), true);
                if (pArray.Length > 0)
                {
                    roles = ((GrantRolesAttribute)pArray[0]).Roles;
                }
                sArray = prop.GetCustomAttributes(typeof(GrantStatesAttribute), true);
                if (sArray.Length > 0)
                {
                    states = ((GrantStatesAttribute)sArray[0]).States;
                }
                sProperty = prop.GetCustomAttributes(typeof(GrantPropertyAttribute), true);
                if (sProperty.Length > 0)
                {
                    propertyGrant = ((GrantPropertyAttribute)sProperty[0]).Property;
                }
            }
            if (method.Name.StartsWith("get_"))
                isGetMethod = true;
            base.RuntimeInitialize(method);
        }
    }
    #endregion
    #region DataFlow
    /// <summary>
    /// <para>Must be specified when the Jazz object contains 'Rules' or when 'Enhanced Persistence' is required.</para>
    /// <para>This attribute must the used in conjunction with the 'Jazz' attribute.</para>
    /// </summary>
    [Serializable]
    [MulticastAttributeUsage(MulticastTargets.Field)]
    public class ObservableAttribute : LocationInterceptionAspect
    {
        private bool isExecutingRule = false;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        public override void OnSetValue(LocationInterceptionArgs args)
        {
            string name = args.LocationName;
            object o = args.Instance;
            
            base.OnSetValue(args);
         
            if (args.Instance == null) return;
            jObject wf = args.Instance as jObject;
            if (!isExecutingRule && wf.Class != null && wf.Class.Rules.Count >= 1)
            {
                isExecutingRule = true;
                try
                {
                    wf.Class.Rules.ExecuteRules(wf);
                }
                finally
                {
                    isExecutingRule = false;
                }
            }
        } 
    }
    #endregion
    #region Verbose
    /// <summary>
    /// *** Not Functional Yet ***
    /// Generates 'Verbose' messages to the console for all activity of specific 
    /// member.  This attribute is independent of the client nexus settings.
    /// <para>This attribute is intended to be use in debugging.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Method| AttributeTargets.Field)]
    public class VerboseAttribute : Attribute
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="o"></param>
        public VerboseAttribute(object o)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        public VerboseAttribute() { }
    }
    #endregion
    #region method test
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class methodTestAttribute: OnMethodBoundaryAspect
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="args"></param>
        public override void OnEntry(MethodExecutionArgs args)
        {
            object o = args.Instance;
            Console.WriteLine(string.Format("entered method - {0}", args.Method.Name));
        }
    }
    #endregion
    //
    //  Marker attributes
    //
    #region UIAttribute
    /// <summary>
    /// Indicates that a field, property or method is to be visible to application.
    /// <para>This is a convenience attribute and has no impact on Jazz functionality.  
    /// The purpose of this attribute is to allow the Jazz object class designer to pass information to application.
    /// Its usage is purely by convention.</para>
    /// <para>Tag objects can be associated with an UI attribute.  This allows the Jazz object designer to associate true objects with a property or method.
    /// These Tag objects can contain information such as mouse over information, display names, a Url for more information, etc.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Property|AttributeTargets.Method, AllowMultiple=false)]
    public class UIAttribute : Attribute
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tagObjectName">The name ('className.memberName') of a static field or property that supplies the tag object.</param>
        public UIAttribute(string tagObjectName)
        {
            this.TagName = tagObjectName;
        }
        /// <summary>
        /// 
        /// </summary>
        public string TagName { get; private set; }
        /// <summary>
        /// 
        /// </summary>
        public UIAttribute()
        {
            this.TagName = "";
        }
    }
    #endregion
    #region SerializeIgnoreAttribute
    /// <summary>
    /// Indicates that the field or property should not be serialized.
    /// <para>This is a marker attribute only.</para>
    /// <para>This is a marker marker is useful for creating custom serializers.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field| AttributeTargets.Event, AllowMultiple = false)]
    public class SerializeIgnoreAttribute : Attribute
    {
        
    }
    #endregion
    #region BaseFieldAttribute
    /// <summary>
    /// Indicates that the field is a '<see cref="jObject"/>' base field.
    /// <para>This is a marker attribute only.</para>
    /// <para>This is a marker marker is useful for creating custom serializers.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property, AllowMultiple = false)]
    public class BaseFieldAttribute : Attribute
    {

    }
    #endregion
    #region AccessorMethodAttribute
    /// <summary>
    /// Indicates that that the method is allowed to execute when the 
    /// object is 'read only' without incurring an exception.
    /// <para>
    /// This attribute should only be applied to methods that will
    /// not change the internal state of the object.
    /// </para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Method| AttributeTargets.Property , AllowMultiple = false)]
    public class AccessorMethodAttribute : Attribute
    {
    }
    #endregion
    #region DoNotAutoInitializeAttribute
    /// <summary>
    /// Indicates that the field or property should not be automatically initialized.
    /// </summary>
    [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property, AllowMultiple = false)]
    public class DoNotAutoInitializeAttribute : Attribute
    {
    }
    #endregion

    #region EmptyObjectAttribute
    /// <summary>
    /// Specifies class of the empty object.
    /// <para>This allows the auto initialization code to determine the 
    /// which 'empty' object to use when initializing fields or properties with declaration type of
    /// abstract '<see cref="jObject"/>' child classes or interfaces.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
    public class EmptyObjectAttribute : Attribute
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="className">Specifies class for the empty object.</param>
        public EmptyObjectAttribute(string className)
        {
            this.ClassName = className;
        }
        /// <summary>
        /// Returns the class name of the empty object.
        /// </summary>
        public string ClassName
        {
            get;
            private set;
        }
    }
    #endregion
    #region UseEmptyObjectForAttribute
    /// <summary>
    /// Specifies that the empty object for the decorated class to be used as the empty object for the specified 'classTypes'.
    /// <para>The auto initialization code will use
    /// the 'empty' object of the decorated class when initializing fields or properties with declaration type of
    /// the specified classType(s).</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class UseEmptyObjectForAttribute : Attribute
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="classTypes">Specifies class(es) or interface(s) that should use the empty object of the decorated class.</param>
        public UseEmptyObjectForAttribute(params Type[] classTypes)
        {
            this.ClassTypes = classTypes;
            foreach (Type t in classTypes)
            {
                if (!(t.IsAbstract || t.IsInterface))
                {
                    throw new ArgumentException(string.Format("The type '{0}' in neither abstract or an interface.", t.FullName), "classTypes");
                }
            }
        }
        /// <summary>
        /// Returns the class name of the empty object.
        /// </summary>
        public Type[] ClassTypes
        {
            get;
            private set;
        }
    }
    #endregion
    #region EmptySingletonAttribute
    /// <summary>
    /// Specifies that this class has only one instance and that instance is
    /// an 'Empty' object.
    /// <para>
    /// That instance is used as an empty object.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class EmptySingletonAttribute : Attribute
    {
    }
    #endregion
    //
    //
    //
    #region Root
    /// <summary>
    /// Determines if instances of this Jazz class should be a root objects.
    /// <para>Root objects are not collected by nexus server garbage collector if they have no references.</para>
    /// <para>Root objects must be deleted before they are collected by the server garbage collector.</para>
    /// <para>This attribute has no meaning if the system does not contain a server nexus for persistence of Jazz objects.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class RootAttribute : Attribute
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jazzObjectType"></param>
        /// <returns></returns>
        public static bool IsRootValue(Type jazzObjectType)
        {
            object[] attArray = jazzObjectType.GetCustomAttributes(typeof(RootAttribute), true);
            if (attArray.Length > 0)
                return ((RootAttribute)attArray[0]).IsRoot;
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        public RootAttribute()
        {
            this.IsRoot = true;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="isRoot"></param>
        public RootAttribute(bool isRoot)
        {
            this.IsRoot = isRoot;
        }
        /// <summary>
        /// 
        /// </summary>
        public bool IsRoot
        {
            get;
            private set;
        }
    }
    #endregion
    #region UniqueName
    /// <summary>
    /// Determines if the instances of this <see cref="jObject"/> class should have unique names.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class UniqueNameAttribute : Attribute
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jazzObjectType"></param>
        /// <returns></returns>
        public static bool IsNameUniqueValue(Type jazzObjectType)
        {
            object[] attArray = jazzObjectType.GetCustomAttributes(typeof(UniqueNameAttribute), true);
            if (attArray.Length > 0)
                return ((UniqueNameAttribute)attArray[0]).IsNameUnique;
            return false;
        }
        /// <summary>
        /// Constructor
        /// </summary>
        public UniqueNameAttribute()
        {
            this.IsNameUnique = true;
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="isNameUnique"></param>
        public UniqueNameAttribute(bool isNameUnique)
        {
            this.IsNameUnique = isNameUnique;
        }
        /// <summary>
        /// 
        /// </summary>
        public bool IsNameUnique
        {
            get;
            private set;
        }
    }
    #endregion
    //
    // States
    //
    #region StateAttribute
    /// <summary>
    /// Specifies that the instance method is to be treated as a Jazz object 'state'.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class StateAttribute : Attribute
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tagObjectName">The name ('className.memberName') of a static field or property that supplies the tag object.</param>
        public StateAttribute(string tagObjectName)
        {
            this.TagName = tagObjectName;
        }
        /// <summary>
        /// 
        /// </summary>
        public string TagName { get; private set; }
        /// <summary>
        /// 
        /// </summary>
        public StateAttribute()
        {
            this.TagName = "";
        }
    }
    #endregion
    #region FinalState Attribute
    /// <summary>
    /// Specifies that the instance method is to be treated as a final or end 'state'.
    /// <para>Jazz objects may have multiple final states.  "Final" states can be used by
    /// the application to indicate that the Jazz object is completed.</para>
    /// <para>"Final" states are treated the same as ordinary 'states'.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
    public class FinalStateAttribute : StateAttribute
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tagObjectName">The name ('className.memberName') of a static field or property that supplies the tag object.</param>
        public FinalStateAttribute(string tagObjectName)
        :base(tagObjectName)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        public FinalStateAttribute()
        :base()
        {
        }
    }
    #endregion
    #region StartState Attribute
    /// <summary>
    /// Specifies that the instance method is to be treated as a Jazz object start 'state'.
    /// <para>When a Jazz object is created it will automatically be placed in the start state.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class StartStateAttribute : StateAttribute
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tagObjectName">The name ('className.memberName') of a static field or property that supplies the tag object.</param>
        public StartStateAttribute(string tagObjectName)
        :base(tagObjectName)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        public StartStateAttribute()
        :base()
        {
        }
    }
    #endregion
    #region StateFromAttribute
    /// <summary>
    /// Specifies the state transistion table.
    /// <para>This is a optional attribute that when included provides an extra level
    /// of checking and a state transistion table for use by application code.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple=true)]
    public class StateToAttribute : Attribute
    {
        /// <summary>
        /// 
        /// </summary>
        public StateToAttribute()
        {
            this.ToState = "";
            this.Via = "";
        }
        /// <summary>
        /// 
        /// </summary>
        public string Via { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string ToState { get; set; }
    }
    #endregion
    //
    // Rule
    //
    #region RuleAttribute
    /// <summary>
    /// Indicates a 'rule' method that is automatically executed.  
    /// <para>When testing conditions in a rules only instance properites and fields should be used. </para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class RuleAttribute : Attribute
    {
    }
    #endregion
    //
    // Grant
    //
    #region GrantStatesAttribute
    /// <summary>
    /// When one of the specified 'states' is equal to the current state of this jObject access will be granted to the method, property, property setter or getter.
    /// <para>When this attribute is applied with other Grant attributes all conditions (logical AND) must be true to Grant access.</para>
    /// <para>This attribute can only be applied to instance members.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
    public class GrantStatesAttribute :Attribute
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="states">Enter the names of state method(s) as string.  For example '[GrantStates("Start", "Update")'].</param>
        public GrantStatesAttribute(params string[] states)
        {
            if (states.Length == 0)
                throw new ArgumentException("A least a single state must be specified.");
            this.States = states;
        }
        /// <summary>
        /// 
        /// </summary>
        public string[] States { get; private set; }
    }
    #endregion
    #region GrantRoles
    /// <summary>
    /// The one of the specified 'roles' is equal to one of the role(s) that the 'logged in' user that access will be granded to the method, property, property setter or getter.
    /// <para>When this attribute is applied with other Grant attributes all conditions (logical AND) must be true to Grant access.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property|AttributeTargets.Method, AllowMultiple = false)]
    public class GrantRolesAttribute : Attribute
    {
        /// <summary>
        /// This attribute specifies the role(s) that the 'logged in' user must posses that will grant access to the method, property, property setter or getter.
        /// <para>When this attribute is applied withother Grant attributes all conditions (logical AND) must be true to Grant access.</para>
        /// </summary>
        /// <param name="roles"></param>
        public GrantRolesAttribute(params string[] roles)
        {
            if (roles.Length == 0)
                throw new ArgumentException("A least a single role must be specified.");
            this.Roles = roles;
        }
        /// <summary>
        /// 
        /// </summary>
        public string[] Roles{get; private set;}
    }
    #endregion
    #region GrantPropertyAttribute
    /// <summary>
    /// GrantProperty attribute grants access based on the value of the specified property.
    /// <para>The property may be of type 'bool', 'IjUser', 'IEnumerable jRole',
    /// 'IEnumerable IjUser' or 'IEnumerable jState'.</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
    public class GrantPropertyAttribute : Attribute
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="property"></param>
        public GrantPropertyAttribute(string property)
        {
            if (property== null || property== "")
                throw new ArgumentException("A single property must be specified.");
            this.Property = property;
        }
        /// <summary>
        /// Returns a string representing the 'Grant Property' name.
        /// </summary>
        public string Property { get; private set; }
    }
    #endregion
}
